﻿using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MessageBox = System.Windows.MessageBox;
using Path = System.IO.Path;

namespace AnalysisPLC
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            InitResult();
        }

        private string _SelectFolderPath;

        public string SelectFolderPath
        {
            get
            {
                return _SelectFolderPath;
            }
            set
            {
                _SelectFolderPath = value;
                PropertyChanged?.Invoke(this, new("SelectFolderPath"));
            }
        }

        private int _StartPos;

        public int StartPos
        {
            get
            {
                return _StartPos;
            }
            set
            {
                _StartPos = value;
                PropertyChanged?.Invoke(this, new("StartPos"));
            }
        }

        private int _EndPos = short.MaxValue;

        public int EndPos
        {
            get
            {
                return _EndPos;
            }
            set
            {
                _EndPos = value;
                PropertyChanged?.Invoke(this, new("EndPos"));
            }
        }


        private int _InputDValue;

        public int InputDValue
        {
            get
            {
                return _InputDValue;
            }
            set
            {
                _InputDValue = value;
                PropertyChanged?.Invoke(this, new("InputDValue"));
            }
        }

        private DateTime _MaxYTime;

        public DateTime MaxYTime
        {
            get
            {
                return _MaxYTime;
            }
            set
            {
                _MaxYTime = value;
                PropertyChanged?.Invoke(this, new("MaxYTime"));
            }
        }
        private DateTime _MinYTime;

        public DateTime MinYTime
        {
            get
            {
                return _MinYTime;
            }
            set
            {
                _MinYTime = value;
                PropertyChanged?.Invoke(this, new("MinYTime"));
            }
        }

        private int _MaxY;

        public int MaxY
        {
            get
            {
                return _MaxY;
            }
            set
            {
                _MaxY = value;
                PropertyChanged?.Invoke(this, new("MaxY"));
            }
        }

        private int _MinY;

        public int MinY
        {
            get
            {
                return _MinY;
            }
            set
            {
                _MinY = value;
                PropertyChanged?.Invoke(this, new("MinY"));
            }
        }


        private int _MaxYOffAll;

        public int MaxYOffAll
        {
            get
            {
                return _MaxYOffAll;
            }
            set
            {
                _MaxYOffAll = value;
                PropertyChanged?.Invoke(this, new("MaxYOffAll"));
            }
        }




        List<string> allFiles = new List<string>();
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            using (var folderDialog = new FolderBrowserDialog())
            {
                System.Windows.Forms.DialogResult result = folderDialog.ShowDialog();
                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    string folderPath = folderDialog.SelectedPath;

                    if (Directory.Exists(folderPath))
                    {
                        SelectFolderPath = folderPath;
                    }
                }
            }
        }
        public static int ShortsToInt(short l, short h)
        {
            return (h << 16) | (l & 0xFFFF);
        }
        public static short ParseMsgShort(ref byte[] bytes, ref int offset)
        {
            short res = BitConverter.ToInt16(bytes, offset);
            offset += 2;
            return res;
        }

        public static int ParseMsgInt(ref byte[] bytes, ref int offset)
        {
            int res = BitConverter.ToInt32(bytes, offset);
            offset += 4;
            return res;
        }

        /// <summary>
        /// byte 转换为 short数组
        /// </summary>
        /// <param name="bytes">源byte数组</param>
        /// <param name="offset">起始下标</param>
        /// <returns></returns>
        public static short[] ByteToShorts(byte[] bytes, int offset, int size)
        {
            short[] res = new short[size / 2];
            for (int i = 0; i < size / 2; i++)
            {
                res[i] = BitConverter.ToInt16(bytes, offset + i * 2);
            }
            return res;
        }

        /// <summary>
        /// 获取回放文件的内容
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private List<(DateTime Time, byte[] FrameData)> AnalysisReplayFile(string path)
        {
            List<(DateTime Time, byte[] FrameData)> result = new();
            byte[] bytes = File.ReadAllBytes(path);

            //var firstFrameLength = BitConverter.ToInt32(bytes, 6);
            //byte[] timeFuff = bytes[10..26];
            //var time = ParseMsgDatetime(ref timeFuff, 0);
            int position = 0;
            while (true)
            {
                try
                {
                    if (bytes.Length < (position + 6))
                    {
                        //文件读完
                        break;
                    }
                    //帧长度
                    var frameLength = BitConverter.ToInt32(bytes, position + 6);

                    int startPos = position;
                    int endPos = startPos + frameLength;
                    byte[] frameBytes = bytes[startPos..endPos];

                    //写入时间
                    byte[] timeFuff = bytes[(position + 10)..(position + 26)];
                    int offset = 0;
                    var time = ParseMsgDatetime(ref timeFuff, ref offset);

                    //当前帧完整buff
                    byte[] contentBytes = frameBytes[42..frameBytes.Length];
                    result.Add((time, contentBytes));
                    //byte[] replayFlag_buff = bytes[startPos..(startPos + 6)];
                    //var replayFlag = Encoding.Default.GetString(replayFlag_buff);

                    position += frameLength;
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }

            }

            return result;
        }

        private readonly string DatetimeFormat = "yyyy-MM-dd HH:mm:ss.fff";


        public DateTime ParseMsgDatetime(ref byte[] bytes, ref int offset)
        {
            DateTime res = DateTime.Now;
            try
            {
                int year = BitConverter.ToInt16(bytes, offset);
                int month = BitConverter.ToInt16(bytes, offset + 2);
                int day = BitConverter.ToUInt16(bytes, offset + 6);
                int hour = BitConverter.ToUInt16(bytes, offset + 8);
                int minute = BitConverter.ToUInt16(bytes, offset + 10);
                int second = BitConverter.ToUInt16(bytes, offset + 12);
                int millisecond = BitConverter.ToUInt16(bytes, offset + 14);
                string dateString = $"{year:D4}-{month:D2}-{day:D2} {hour:D2}:{minute:D2}:{second:D2}.{millisecond:D3}";

                if (DateTime.TryParseExact(dateString, DatetimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out res))
                {
                    // Parsing successful

                }
                else
                {
                    // Use a default value or handle the failure case
                    res = DateTime.MinValue; // Set to a default value
                }
            }
            catch (Exception ex)
            {

            }
            offset += 16;
            return res;
        }

        private bool _CanStart = true;

        public bool CanStart
        {
            get
            {
                return _CanStart;
            }
            set
            {
                _CanStart = value;
                PropertyChanged?.Invoke(this, new("CanStart"));
            }
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            Task.Run(async () =>
            {
                CanStart = false;

                if (Directory.Exists(SelectFolderPath))
                {
                    var files = Directory.GetFiles(SelectFolderPath, "RP_WriteToPLC*.txt");
                    List<FileData> fileDatas = new();
                    foreach (var file in files)
                    {
                        FileData fileData = new();
                        fileDatas.Add(fileData);
                        List<(DateTime Time, byte[] FrameData)> CurrentFileAllFrame = AnalysisReplayFile(file);
                        List<PLCData> plcDataList = new();
                        fileData.AllFrame = CurrentFileAllFrame;
                        fileData.AllFramePLCData = plcDataList;

                        foreach (var item in CurrentFileAllFrame)
                        {
                            PLCData model = new();
                            int plcSize = BitConverter.ToInt32(item.FrameData);

                            int offset = 0;
                            byte[] memWriteToPLC = item.FrameData[4..item.FrameData.Length];//WriteToPLC共享内存的数据
                            short craneNo = ParseMsgShort(ref memWriteToPLC, ref offset);
                            DateTime WriteTime = ParseMsgDatetime(ref memWriteToPLC, ref offset);
                            offset += 42;//Spare*42
                            short baseAddr = ParseMsgShort(ref memWriteToPLC, ref offset);
                            short registerCount = ParseMsgShort(ref memWriteToPLC, ref offset);

                            var plcData = ByteToShorts(memWriteToPLC, 64, memWriteToPLC.Length - 64);//写到plc的数据

                            int HealthyCounter = ShortsToInt(plcData[0], plcData[1]);

                            int craneNo1 = plcData[2];
                            short UseFlag = plcData[3];
                            short MeasureStatus = plcData[4];
                            short PointInterval = plcData[5];
                            short StartPosition = plcData[6];
                            short PointCount = plcData[7];

                            var posY = plcData[40..707];

                            model.WriteTime = WriteTime;
                            model.PlcData = plcData;

                            model.PointInterval = PointInterval;
                            model.StartPosition = StartPosition;
                            model.PointCount = PointCount;
                            model.PosY = posY;
                            plcDataList.Add(model);
                        }
                    }

                    var all = fileDatas.SelectMany(p => p.AllFramePLCData).ToList();
                    InitResult();
                    AllFrame = all.ToList();
                    await Task.Delay(100);

                    // MaxYOffAll = all.SelectMany(p => p.PosY).Max(p => p);

                    short preMin = short.MaxValue;
                    DateTime preMinTime = DateTime.MinValue;
                    short PreMax = short.MinValue;
                    DateTime preMaxTime = DateTime.MinValue;



                    for (int i = 0; i < all.Count; i++)
                    {
                        int startIndex = (StartPos - all[i].StartPosition) / all[i].PointInterval;
                        int endIndex = (EndPos - all[i].StartPosition) / all[i].PointInterval;

                        //筛选出范围
                        var posy = all[i].PosY.Skip(startIndex).Take(endIndex - startIndex).Where(p => p != -9999).ToArray();
                        if (posy.Length > 0)
                        {
                            var min = posy.Min();
                            var max = posy.Max();
                            if (preMin > min)
                            {
                                preMin = min;
                                preMinTime = all[i].WriteTime;
                            }
                            if (PreMax < max)
                            {
                                PreMax = max;
                                preMaxTime = all[i].WriteTime;
                            }

                            if ((PreMax - preMin) > InputDValue)
                            {
                                MinYTime = preMinTime;
                                MinY = min;

                                MaxYTime = preMaxTime;
                                MaxY = PreMax;
                                CanStart = true;
                                return;
                            }
                        }
                    }
                }

                CanStart = true;
            });

        }

        private void InitResult()
        {
            MinYTime = DateTime.MinValue;
            MinY = -999;
            MaxYTime = DateTime.MinValue;
            MaxY = 999;
            MaxYOffAll = 0;
            AllFrame.Clear();
        }

        List<PLCData> AllFrame = new();
        private void ExportCSV_Click(object sender, RoutedEventArgs e)
        {
            if (AllFrame.Count == 0)
            {
                MessageBox.Show("请先计算");
                return;
            }

            Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
            dialog.Title = "导出CSV";
            dialog.FileName = $"PLC扫描点.csv";
            dialog.InitialDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory);
            dialog.Filter = "CSV File(*.csv)|*.csv"; // 设置过滤器只显示CSV文件  
            if (dialog.ShowDialog() == true)
            {
                var csvFilePath = dialog.FileName;

                if (AllFrame.Count > 0)
                {
                    int maxPointCount = AllFrame.Max(p => p.PointCount);

                    // 使用StreamWriter写入CSV文件  
                    using (StreamWriter writer = new StreamWriter(csvFilePath, false, Encoding.UTF8))
                    {
                        //添加表头
                        string header = string.Empty;
                        header += "WriteTime,";//"Sys"
                        header += "PointInterval,";//"Time"
                        header += "StartPosition,";//"Time"
                        header += "PointCount";//"CraneID"
                        for (int i = 0; i < maxPointCount; i++)
                        {
                            header += $",Point{i}";
                        }
                        writer.WriteLine(header);

                        //添加每一帧的数据
                        for (int i = 0; i < AllFrame.Count; i++)
                        {
                            try
                            {
                                string line = "";

                                PLCData frame = AllFrame[i];
                                line += frame.WriteTime + ",";
                                line += frame.PointInterval + ",";
                                line += frame.StartPosition + ",";
                                line += frame.PointCount;

                                for (int j = 0; j < frame.PointCount; j++)
                                {
                                    if (j < frame.PosY.Length)
                                    {
                                        line += $",{frame.PosY[j]}";
                                    }
                                }
                                writer.WriteLine(line);
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show(ex.ToString());
                            }
                        }
                    }

                    MessageBox.Show("成功导出CSV文件");

                    string dir = Path.GetDirectoryName(csvFilePath);
                    // 使用Process.Start打开文件夹  
                    Process.Start("explorer.exe", dir);
                }
            }

        }
    }

    public class FileData
    {
        public List<(DateTime Time, byte[] FrameData)> AllFrame;
        public List<PLCData> AllFramePLCData;
    }

    public class PLCData
    {
        public short[] PlcData;
        public DateTime WriteTime;

        public short PointInterval;
        public short StartPosition;
        public short PointCount;
        public short[] PosY;
    }
}